﻿using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Security;

namespace CloudStorageLight.Core
{
    public class CloudAccount : TableEntity
    {
        public CloudAccount()
        {
            CanFullTextSearch = false;
            CanCreateSelfAccount = false;
            CanUseBackupFunction = true;
            CanUseAutoPassword = true;
            CanUseUserPasswordProtect = true;
            CanUseTransferFile = false;           
            CanUseRMS = false;
            CanUseGuestAccount = false;
            LogRetentionDays = 365;
            ClientDownloadPage = "disable";
        }

        public CloudAccount(string name):this()
        {
            this.PartitionKey = "";
            this.RowKey = name;
        }

        /// <summary>
        /// アカウント名
        /// </summary>
        public string AccountName { get { return RowKey; } }

        /// <summary>
        /// アカウント種類
        /// </summary>
        public string AccountType { get; set; }

        /// <summary>
        /// メールドメイン
        /// </summary>
        public string MailDomain { get; set; }

        /// <summary>
        /// SharePoint Host URL
        /// </summary>
        public string SpHostUrl { get; set; }

        /// <summary>
        /// 作成者のEMailアドレス
        /// </summary>
        public string EMailAddress { get; set; }


        /// <summary>
        /// Azure ADの利用
        /// </summary>
        public bool AzureAdMode { get; set; }

        /// <summary>
        /// Azure Authority
        /// </summary>
        public string Authority { get; set; }

        /// <summary>
        /// Azure アプリケーション Client ID
        /// </summary>
        public string CloudStorageLightClientId { get; set; }

        /// <summary>
        /// Azure アプリケーション Client Key
        /// </summary>
        public string CloudStorageLightAppKey { get; set; }

        /// <summary>
        /// Azure AD テナントID
        /// </summary>
        public string TenantId { get; set; }

        /// <summary>
        /// RMSアクセス用に作成したサービスプリンシパルの名前
        /// </summary>
        public string RmsServicePrincipal { get; set; }

        /// <summary>
        /// RMSアクセス用に作成したサービスプリンシパルのキー
        /// </summary>
        public string RmsSymmetricKey { get; set; }


        /// <summary>
        /// Rms Extranet Url 
        /// </summary>
        public string RmsExtranetUrl { get; set; }

        /// <summary>
        /// Rms Intranet Url
        /// </summary>
        public string RmsIntranetUrl { get; set; }

        /// <summary>
        /// 管理者
        /// </summary>
        public string Administrators { get; set; }

        /// <summary>
        /// 有効なメールアドレス
        /// </summary>
        public string AcceptMailAddressPattern { get; set; }

        /// <summary>
        /// 有効なゲスト・メールアドレス
        /// </summary>
        public string GuestMailAddressPattern { get; set; }

        /// <summary>
        /// 有効なIPアドレス
        /// </summary>
        public string AllowIPAddresses { get; set; }

        /// <summary>
        /// 端末登録時IPアドレスで有効化を判断
        /// </summary>
        public bool UseClientTokenForIPCheck { get; set; }

        /// <summary>
        /// ストレージの構成情報
        /// </summary>
        public string Config { get; set; }

        /// <summary>
        /// エクスプローラ機能のダウンロードページ
        /// </summary>
        public string ClientDownloadPage { get; set; }

        /// <summary>
        /// 全文検索を利用できるようにする
        /// </summary>
        public bool CanFullTextSearch { get; set; }

        /// <summary>
        /// アカウント作成をユーザ自身でできるようにする
        /// </summary>
        public bool CanCreateSelfAccount { get; set; }

        /// <summary>
        /// ファイル履歴を利用できるようにする
        /// </summary>
        public bool CanUseBackupFunction { get; set; }

        /// <summary>
        /// ファイルダウンロード時にパスワード設定できるようにする
        /// </summary>
        public bool CanUseAutoPassword { get; set; }

        /// <summary>
        /// RMSを利用できるようにする
        /// </summary>
        public bool CanUseRMS { get; set; }

        /// <summary>
        /// 外部ユーザを利用できるようにする
        /// </summary>
        public bool CanUseGuestAccount { get; set; }

        /// <summary>
        /// ユーザパスワードでファイルを保護できるようにする
        /// </summary>
        public bool CanUseUserPasswordProtect { get; set; }

        /// <summary>
        /// クラウドアカウントを利用できないようにロックする
        /// </summary>
        public bool IsLocked { get; set; }


        /// <summary>
        /// クライアントにチケットを登録して端末の正当性を確認できるようにする
        /// </summary>
        public bool CanUseDeviceAuthentication { get; set; }

        /// <summary>
        /// ストレージの利用制限
        /// </summary>
        /// <remarks>
        /// UploadとCopyが制限される
        /// </remarks>
        public long? StorageLimitSize { get; set; }

        /// <summary>
        /// 認証チケットのタイムアウト
        /// </summary>
        public double? AuthTicketTimeout
        {
            get { return _AuthTicketTimeout ?? FormsAuthentication.Timeout.TotalMinutes; }
            set { _AuthTicketTimeout = value; }
        }
        private double? _AuthTicketTimeout;

        /// <summary>
        /// ファイル転送機能を利用できる
        /// </summary>
        public bool CanUseTransferFile { get; set; }

        /// <summary>
        /// 全文検索対象の拡張子
        /// </summary>
        public string CrawlableFileExtension { get; set; }

        /// <summary>
        ///  ログ保存期間
        /// </summary>
        public int LogRetentionDays { get; set; }

        /// <summary>
        /// 認証用FTPサーバ
        /// </summary>
        public string AuthFtpServer { get; set; }
        /// <summary>
        /// 認証用ドメイン（FTPサーバで認証するユーザのドメイン）
        /// </summary>
        public string AuthFtpDomain { get; set; }


        public List<string> GetAdministrators()
        {
            if (Administrators == null) return new List<string>();
            return Administrators.Replace("\r","").Split('\n').Where(x => !string.IsNullOrEmpty(x)).Select(x=>x.Trim()).ToList();
        }

        public bool CanAccessIPAddress(string ipAddress)
        {
            if (string.IsNullOrEmpty(AllowIPAddresses)) return true;
            IPAddress ip;
            if (!IPAddress.TryParse(ipAddress, out ip)) return false;
            foreach (var ipItem in AllowIPAddresses.Replace("\r", "").Split('\n'))
            {
                var items = ipItem.Split('-');
                if (items.Length == 1)
                {
                    IPAddress ip1;
                    if (IPAddress.TryParse(items[0], out ip1))
                    {
                        if (ip == ip1) return true;
                    }
                    if (items[0] == ipAddress) return true;
                }
                else
                {
                    IPAddress ip1,ip2;
                    if (IPAddress.TryParse(items[0], out ip1 ) && IPAddress.TryParse(items[1], out ip2 ))
                    {
                        var range = new IPAddressRange(ip1, ip2);
                        if (range.IsInRange(ip)) return true;
                    }
                }
            }
            return false;
        }

        public List<string> GetCrawlableFileExtension()
        {
            if (string.IsNullOrEmpty(CrawlableFileExtension))
            {
                return _CrawlableFileExtension;
            }
            return CrawlableFileExtension.Split(',').Select(x => x.Trim()).ToList();
        }
        private List<string> _CrawlableFileExtension = ".xls,.xlsx,.doc,.docx,.ppt,.pptx,.pdf,.txt".Split(',').ToList();

        public bool CanAccess(string mailaddress, bool includeGuestPattern = true)
        {
            if (string.IsNullOrWhiteSpace(mailaddress)) return false;

            //作成者OK
            if (string.Equals(EMailAddress, mailaddress, StringComparison.InvariantCultureIgnoreCase)) return true;

            //管理者の指定ユーザはOK
            foreach (var admin in GetAdministrators())
            {
                if (string.Equals(admin, mailaddress, StringComparison.InvariantCultureIgnoreCase)) return true;
            }

            if (string.IsNullOrWhiteSpace(AcceptMailAddressPattern))
            {
                //ホワイトリストが指定されていない場合はメールドメインをチェック
                if (!string.IsNullOrEmpty(MailDomain) && mailaddress.EndsWith(MailDomain, StringComparison.InvariantCultureIgnoreCase)) return true;
            }
            else
            {
                //ホワイトリストが指定されている場合
                foreach (var pattern in AcceptMailAddressPattern.Replace("\r", "").Split('\n').Where(x => !string.IsNullOrEmpty(x)))
                {
                    if (Regex.IsMatch(mailaddress, "^" + pattern.TrimStart('^').TrimEnd('$') + "$", RegexOptions.IgnoreCase))
                    {
                        return true;
                    }
                }
            }
            if (includeGuestPattern && !string.IsNullOrWhiteSpace(GuestMailAddressPattern))
            {
                //ホワイトリストが指定されている場合
                foreach (var pattern in GuestMailAddressPattern.Replace("\r", "").Split('\n').Where(x => !string.IsNullOrEmpty(x)))
                {
                    if (Regex.IsMatch(mailaddress, "^" + pattern.TrimStart('^').TrimEnd('$') + "$", RegexOptions.IgnoreCase))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public bool IsFtpAuthentication()
        {
            return !string.IsNullOrEmpty(this.AuthFtpDomain) && !string.IsNullOrEmpty(this.AuthFtpServer);
        }

        public void Save(string connectionString)
        {
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
            TableBatchOperation batchOperation = new TableBatchOperation();
            var table = tableClient.GetTableReference("CloudAccount");
            table.CreateIfNotExists();
            batchOperation.InsertOrReplace(this);
            table.ExecuteBatch(batchOperation);
        }

        public static bool Delete(string connectionString, string name)
        {
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
            TableBatchOperation batchOperation = new TableBatchOperation();
            var table = tableClient.GetTableReference("CloudAccount");
            table.CreateIfNotExists();

            var query = new TableQuery<CloudAccount>();
            query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
            query = query.Where(TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, name));
            var list = table.ExecuteQuery(query);
            var account = list.FirstOrDefault();
            if (account == null) return false;
            var result = table.Execute(TableOperation.Delete(account));
            return true;
        }


        public static CloudAccount Load(string connectionString, string name)
        {
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
            TableBatchOperation batchOperation = new TableBatchOperation();
            var table = tableClient.GetTableReference("CloudAccount");
            table.CreateIfNotExists();

            var query = new TableQuery<CloudAccount>();
            query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
            query = query.Where(TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, name));
            var list = table.ExecuteQuery(query);
            return list.FirstOrDefault();

        }

        public static List<CloudAccount> Load(string connectionString)
        {
            var storageAccount = CloudStorageAccount.Parse(connectionString);
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
            TableBatchOperation batchOperation = new TableBatchOperation();
            var table = tableClient.GetTableReference("CloudAccount");
            table.CreateIfNotExists();

            var query = new TableQuery<CloudAccount>();
            query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
            var list = table.ExecuteQuery(query);
            return list.ToList();

        }

        //public static CloudAccount LoadByEmail(string connectionString, string email)
        //{
        //    var storageAccount = CloudStorageAccount.Parse(connectionString);
        //    CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
        //    TableBatchOperation batchOperation = new TableBatchOperation();
        //    var table = tableClient.GetTableReference("CloudAccount");
        //    table.CreateIfNotExists();

        //    var query = new TableQuery<CloudAccount>();
        //    query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
        //    query = query.Where(TableQuery.GenerateFilterCondition("EMailAddress", QueryComparisons.Equal, email));
        //    var list = table.ExecuteQuery(query);
        //    return list.FirstOrDefault();
        //}

        //public static CloudAccount LoadPrivateAccount(string connectionString, string email)
        //{
        //    var storageAccount = CloudStorageAccount.Parse(connectionString);
        //    CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
        //    TableBatchOperation batchOperation = new TableBatchOperation();
        //    var table = tableClient.GetTableReference("CloudAccount");
        //    table.CreateIfNotExists();

        //    var query = new TableQuery<CloudAccount>();
        //    query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
        //    query = query.Where(TableQuery.GenerateFilterCondition("EMailAddress", QueryComparisons.Equal, email));
        //    var list = table.ExecuteQuery(query);
        //    return list.Where(x=>x.AccountType == "Private").FirstOrDefault();
        //}


        //public static CloudAccount LoadByMailDomain(string connectionString, string domain)
        //{
        //    var storageAccount = CloudStorageAccount.Parse(connectionString);
        //    CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
        //    TableBatchOperation batchOperation = new TableBatchOperation();
        //    var table = tableClient.GetTableReference("CloudAccount");
        //    table.CreateIfNotExists();

        //    var query = new TableQuery<CloudAccount>();
        //    query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
        //    query = query.Where(TableQuery.GenerateFilterCondition("MailDomain", QueryComparisons.Equal, domain));
        //    var list = table.ExecuteQuery(query);
        //    return list.FirstOrDefault();

        //}

        public static CloudAccount LoadBySpHostUrl(string connectionString, string spHostUrl)
        {
            if (spHostUrl == null) return null;

            var storageAccount = CloudStorageAccount.Parse(connectionString);
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
            TableBatchOperation batchOperation = new TableBatchOperation();
            var table = tableClient.GetTableReference("CloudAccount");
            table.CreateIfNotExists();

            var query = new TableQuery<CloudAccount>();
            query = query.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, ""));
            var list = table.ExecuteQuery(query);
            return list.Where(x => x.SpHostUrl != null && spHostUrl.StartsWith(x.SpHostUrl)).FirstOrDefault();

        }
    }
}
